#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/tcp.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <time.h>
#include <stdint.h>
#include <unistd.h>
#include "flow_tag.h"
#define DESTINATION_IP "127.0.0.1"
char random_src_ip[20];
char random_dst_ip[20];
extern void random_ipv4(char *src_ip, char *dst_ip);
extern unsigned short get_ipv4_checksum(unsigned short *ptr, int len);
extern unsigned short get_ipv4_tcp_checksum(unsigned short *addr, int len, in_addr_t saddr, in_addr_t daddr);
extern uint64_t random_num(uint64_t start, uint64_t end);
int main(int argc,char **argv) {
    srand(time(NULL)); 
    int sockfd;
    char buffer[4096]={0};
    memset(buffer,0,sizeof(buffer));
    struct iphdr *iph = (struct iphdr *) buffer;
    struct tcphdr *tcph = (struct tcphdr *) (buffer + sizeof(struct iphdr));
    struct flow_tag *tag = (struct flow_tag *) (buffer + sizeof(struct iphdr) + sizeof(struct tcphdr));
    sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
    if (sockfd < 0) {
        perror("socket");
        exit(1);
    }
    memset(buffer, 0, sizeof(buffer));
    iph->ihl = 5;
    iph->version = 4;
    iph->tos = 0;
    iph->tot_len = sizeof(struct iphdr) + sizeof(struct tcphdr) + sizeof(struct flow_tag);
    iph->id = htonl(random_num(100,10000));
    iph->frag_off = 0;
    iph->ttl = random_num(10,20);
    iph->protocol = IPPROTO_TCP;
    tcph->source = htons(random_num(1000,2000));
    tcph->dest = htons(random_num(10000,20000));
    tcph->seq = htonl(random_num(10000,20000));
    tcph->ack_seq = htonl(random_num(10000,20000));
    tcph->doff = 5; 
    tcph->syn = 1;
    tcph->fin = 1;
    tcph->psh = 1;
    tcph->ack = 1;
    tcph->rst = 1;
    tcph->urg = 1;
    tcph->window = htons(random_num(500,1000));
    tcph->urg_ptr = 0;
    struct sockaddr_in sin;
    memset(&sin, 0, sizeof(sin));  
    sin.sin_family = AF_INET;  
    sin.sin_addr.s_addr = inet_addr(DESTINATION_IP); 
    if(argc < 3){
        random_ipv4(random_src_ip,random_dst_ip);
        const char* source_ip = random_src_ip;
        const char* dst_ip = random_dst_ip;
        printf("source ip is %s\n",source_ip);
        printf("destination ip is %s\n",dst_ip);
        iph->saddr = inet_addr(source_ip);
        iph->daddr = inet_addr(dst_ip);
    } 
    else{
        const char* source_ip = argv[1];
        const char* dst_ip = argv[2];
        printf("source ip is %s\n",source_ip);
        printf("destination ip is %s\n",dst_ip);
        iph->saddr = inet_addr(source_ip);
        iph->daddr = inet_addr(dst_ip);
    }
    tag->tag_id[1] = htonl(random_num(1,100000));
    for(int i = 0;i <=7;i++){
        tag->id_hash[i] = random_num(0,0xffffffff);
    	tag->tag_hash[i] = random_num(0,0xffffffff);
    }
    tag->data_id[1] = htonl(random_num(1,100000));
    tag->sent_num[1] = htonl(random_num(1,100000));
    uint64_t protocol_type = 0;
    uint64_t data_mode = random_num(1,15);
    uint64_t level = random_num(0,3)&2;//00 11
    uint64_t dst_id = random_num(0x2000,0x2fff);
    uint64_t src_id = random_num(0x1000,0x1fff);
    uint64_t data = (src_id<<36) + (dst_id<<8) + (level<<6) + (data_mode<<1) + protocol_type;
    tag->sent_num[2] = htonl((data>>32)&0xffffffff);
    tag->sent_num[3] = htonl(data&0xffffffff);
    tag->src_ip[3] = iph->saddr;
    tag->dst_ip[3] = iph->daddr;
    iph->check = get_ipv4_checksum((unsigned short*)buffer,iph->tot_len);
    tcph->check = get_ipv4_tcp_checksum((unsigned short *)tcph,sizeof(struct tcphdr)+sizeof(struct flow_tag),iph->saddr,iph->daddr); 
    if (sendto(sockfd, buffer, iph->tot_len, 0, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
        perror("sendto");
        exit(1);
    }
    close(sockfd);
    return 0;
}